home *** CD-ROM | disk | FTP | other *** search
/ You're the Director / You're The Director.iso / pc / inc / qtmacros.inc < prev    next >
Encoding:
Text File  |  1994-12-19  |  33.8 KB  |  778 lines

  1.  
  2.  
  3. ; ---------------------------------------------------------------------
  4. ;
  5. ; QTMACROS.INC - QuickTime for Windows Helper Macros
  6. ;
  7. ;                Version 1.1
  8. ;
  9. ;                (c) Copyright 1988-1994 Apple Computer, Inc. All Rights Reserved.
  10. ;
  11. ; ---------------------------------------------------------------------
  12.  
  13.  
  14. COMMENT @
  15. Video dispatch codes
  16. @
  17. VDSP_SETBANK     EQU  1
  18. VDSP_SLIDEWINDOW EQU  2
  19. VDSP_SAVECONTEXT EQU  3
  20. VDSP_RESTCONTEXT EQU  4
  21. VDSP_SETTARGET   EQU  5
  22. VDSP_IDENTIFY    EQU 21
  23. VDSP_VERSION     EQU 22
  24. VDSP_BANKTABLE   EQU 23
  25. VDSP_BITBLTTYPE  EQU 24
  26. VDSP_SCANWIDTH   EQU 25
  27. VDSP_TERMINATE   EQU 86
  28.  
  29. COMMENT @
  30. VHDW selectors
  31. @
  32. kSniffVideoHardware EQU 0
  33.  
  34. COMMENT @
  35. BitBlt types for hardware support
  36. @
  37. BBL_NONE         EQU  0           ; unknown BitBlt type
  38. BBL_MOVSD        EQU  1           ; use MOVSD
  39. BBL_DRVR         EQU  2           ; use the driver's BitBlt
  40. BBL_MOVPL16      EQU  3           ; 16-bit planar
  41.  
  42. COMMENT @
  43. BMP types
  44. @
  45. BMP_NONE         EQU  0           ; unknown type
  46. BMP_DIB          EQU  1           ; DIB
  47. BMP_MONO         EQU  2           ; monochrome
  48. BMP_PACKED_4     EQU  3           ; packed 4 bit, e.g., Fahrenheit
  49. BMP_PLANAR_4     EQU  4           ; VGA or SVGA
  50. BMP_INDEX_8      EQU  5           ; palettized driver
  51. BMP_5_5_5        EQU  6           ; 32,768 colors
  52. BMP_5_6_5        EQU  7           ; XGA, Intel order
  53. BMP_PLANAR_16    EQU  8           ; two planes of one byte each
  54. BMP_8_8_8_RGB    EQU  9           ; true color RGB
  55. BMP_MEMERR       EQU 10           ; insufficient memory for buffers
  56. BMP_8_8_8_BGR    EQU 11           ; true color BGR
  57. BMP_5_6_5_M      EQU 12           ; XGA, Motorola order
  58.  
  59. COMMENT @
  60. Macros AlignBP and AlignBPRet:
  61. Align BP on a DWORD boundary
  62.  
  63. On 386DX and 486 CPUs, applications get better performance by accessing
  64. DWORD operands on DWORD boundaries.  Unaligned operands require an extra
  65. bus cycle.  Our tests on various QuickTime assembler functions shows
  66. unaligned operands typically add a 10% peformance penalty to the entire
  67. function.  By being careful, we can ensure that WORD arguments and WORD
  68. local variables occur in pairs, but we cannot guarantee the alignment of
  69. BP, which is used to access these variables.  The alignment of BP is
  70. determined at run time.  MSC 7.0 aligns the stack only on WORD
  71. boundaries.
  72.  
  73. This macro ensures that BP is aligned on a DWORD boundary.  It is
  74. intended only for use with NEAR PROCs that use the PASCAL calling
  75. sequence.
  76.  
  77. For a NEAR PROC the function arguments and local variables are separated
  78. by two WORDs, the return address and the caller's BP.  Thus, an aligned
  79. BP optimizes accesses to both.  For a FAR PROC the function arguments
  80. and local variables are separated by three WORDs, since the return
  81. address is two WORDs.  In that case, we must define a dummy WORD as the
  82. first local variable and misalign BP, so that the memory references will
  83. be aligned.
  84.  
  85. When BP is not aligned, the macro aligns it, then moves the function
  86. arguments, return address and saved BP accordingly.  The PASCAL
  87. restriction allows the macro to find the byte size of the arguments in
  88. the RET instruction.  The application must insert label "AlignBPRet"
  89. immediately after one of its RET instructions.  The application must
  90. also define a dummy WORD as the last local variable.
  91.  
  92. The macro assumes the direction indicator is 0 (forward), that the
  93. application already saved SI and DI (via USES SI DI on the PROC
  94. statement), and that ES and CX can be destroyed.
  95.  
  96. Upon exit from the function, we must adjust the stack pointer if BP was
  97. changed at entry.  We accomplish this by storing in the extra word made
  98. available at the top of the stack the caller's return address.  At the
  99. location in the stack that normally holds the caller's return address,
  100. we place an address in this program.  At the doctored return address, we
  101. execute a near return.
  102.  
  103. Macro AlignBPExit uses a machine instruction for the near RET to prevent
  104. the assembler from generating epilog code.
  105. @
  106. AlignBP  MACRO
  107.          LOCAL   AlreadyAligned
  108.          TEST    BP, 2            ;; Already on DWORD boundary?
  109.          JE      AlreadyAligned   ;; Skip if already aligned
  110.          CMP     BYTE PTR AlignBPRet - 3, 0C2H ;; Expected RET instruction?
  111.          JNE     AlreadyAligned   ;; Don't adjust if environment unknown
  112.          MOV     CX, WORD PTR [AlignBPRet - 2];; Get byte size of parameter list
  113.          SHR     CX, 1            ;; Convert byte count to word count
  114.          ADD     CX, 2            ;; Account for return address and saved BP
  115.          SUB     BP, 2            ;; Perform the alignment ourselves
  116.                                   ;; Requires a word of slop after all local
  117.                                   ;; variables
  118.          PUSH    SS               ;; Copy SS to ES
  119.          POP     ES               ;;
  120.          MOV     DI, BP           ;; Set target pointer
  121.          LEA     SI, [DI+2]       ;; Set source pointer
  122.          CLD
  123.          REP     MOVSW [DI],SS:[SI] ;; Move the arguments down
  124.          MOV     AX, [BP+2]       ;; Get caller's return address
  125.          MOV     ES:[DI], AX      ;; Store at top of stack
  126.          MOV     [BP+2], OFFSET AlignFixSP ;; Substitute our return address
  127. AlreadyAligned:
  128.          ENDM
  129.  
  130. AlignFarBP  MACRO
  131.          LOCAL   AlreadyAligned
  132.          TEST    BP, 2            ;; Already on DWORD boundary?
  133.          JE      AlreadyAligned   ;; Skip if already aligned
  134.          CMP     BYTE PTR AlignFarBPRet - 3, 0CAH ;; Expected RET instruction?
  135.          JNE     AlreadyAligned   ;; Don't adjust if environment unknown
  136.          MOV     CX, WORD PTR [AlignFarBPRet - 2];; Get byte size of parameter list
  137.          SHR     CX, 1            ;; Convert byte count to word count
  138.          ADD     CX, 3            ;; Account for return address:selector and saved BP
  139.          SUB     BP, 6            ;; Perform the alignment ourselves
  140.                                   ;; Requires 6 bytes of slop after all local
  141.                                   ;; variables
  142.          PUSH    SS               ;; Copy SS to ES
  143.          POP     ES               ;;
  144.          MOV     DI, BP           ;; Set target pointer
  145.          LEA     SI, [DI+6]       ;; Set source pointer
  146.          CLD
  147.          REP     MOVSW [DI],SS:[SI] ;; Move the arguments down
  148.          MOV     AX, [BP+2]       ;; Get caller's return address offset
  149.          MOV     ES:[DI], AX      ;; Store at top of stack
  150.          mov     ax,[bp+4]        ;; get caller's return address selector
  151.          mov     es:[di+2], ax    ;; store caller's code selector
  152.          MOV     [BP+2], OFFSET AlignFixSP ;; Substitute our return address
  153. AlreadyAligned:
  154.          ENDM
  155.  
  156. AlignBPExit MACRO
  157. AlignBPRet:                       ;; Immediately follow the RETN immed
  158.          ALIGN   16               ;; Align for better performance
  159. AlignFixSP:
  160.          RETN                     ;; Return to caller without epilog
  161.          ENDM
  162.  
  163. AlignFarBPExit MACRO
  164. AlignFarBPRet:                       ;; Immediately follow the RETF immed
  165.          ALIGN   16               ;; Align for better performance
  166. AlignFixSP:
  167.          RETF                     ;; Return to caller without epilog
  168.          ENDM
  169.  
  170. COMMENT @
  171. Macro BSWAPX:
  172. Exchange the bytes in a DWORD register.
  173.  
  174. On a 486, the BSWAP instruction uses 1 clock instead of the 6 or 7
  175. clocks required for this macro. Unfortunately, there is no cheap
  176. inline way at run time to distinguish between a 386 and 486.
  177. @
  178. BSWAPX   MACRO   reg:REQ
  179. Root     SUBSTR  <reg>, 2, 1
  180. Xchg8L    CATSTR  Root, <L>
  181. Xchg8H    CATSTR  Root, <H>
  182.          xchg    Xchg8L,Xchg8H
  183.          ROL     reg, 16
  184.          xchg    Xchg8L,Xchg8H
  185.          ENDM
  186.  
  187. COMMENT @
  188. Macro to initialize dirty list, initialize bank bounds table, set GS:0
  189. to bank bounds table, set ES:EDI to initial hardware target address,
  190. set the bank for banked adapters.
  191. @
  192. SetHdwTarget MACRO
  193.          LGS     DI, BankBds      ;; Point to bank bounds table
  194.          MOV     DI, GS:[DI]      ;; Get residual block count of first bad row
  195.          MOV     NextBadBlock, DI ;; Save the residual block count to match
  196.  
  197.          MOV     AX, WORD PTR OffScreen ;; Point to dirty list
  198.          REPEAT  BPPT * Mag
  199.          ADD     AX, BWIDTH       ;; Allocate room for offscreen pixels
  200.          ENDM                     ;; of REPEAT  BPPT * Mag
  201.          IF      Mag EQ 2
  202.          ADD     AX, WdBytes      ;; Allocate room for second scan line in pair
  203.          ENDIF
  204.          INC     AX               ;; Round up to even offset for efficiency
  205.          AND     AL, 0FEH         ;;
  206.          ADD     AX, 4            ;; Leave room for post-processing
  207.          MOV     DirtyListFirst, AX ;;
  208.  
  209.          MOV     AX, DESTY        ;; Get starting row
  210.          MUL     WdBytes          ;; Multiply by width of adapter scan line
  211.          REPEAT  BPPT             ;; Bytes per target pixel
  212.          ADD     AX, DESTX        ;; Add in destination column
  213.          ADC     DX, 0            ;; Bump bank number if carry
  214.          ENDM                     ;; of REPEAT BPPT
  215.          PUSH    DX               ;; Push high order word
  216.          PUSH    AX               ;; Push low order word
  217.          PUSH    VDSP_SETTARGET   ;; Dispatch code for SetTarget
  218.          CALL    [Hardware]       ;; Set the initial bank, also ES:DI
  219.          ADD     SP, 6            ;; Remove arguments from the stack
  220.          MOV     Bank, AX         ;; Save the returned bank
  221.          MOV     SelVRAM, ES      ;; Save VRAM selector
  222.          ENDM                     ;; of SetHdwTarget MACRO
  223.  
  224. COMMENT @
  225. Bank change macros:
  226. Used by functions that write directly to video adapter hardware.
  227.  
  228. The bank change macros assume that all bank changes are of size 1.  This
  229. is so, since the program moves at most 4 rows at a time and each bank holds
  230. at least 40 rows.
  231. @
  232. DoBankUp MACRO
  233.          INC     Bank             ;; Go to next bank
  234.          PUSH    Bank             ;; Pass argument to function
  235.          PUSH    VDSP_SETBANK     ;; Dispatch code for SetBank
  236.          CALL    [Hardware]       ;; Set the new bank
  237.          ADD     SP, 4            ;; Remove arguments from the stack
  238.          ENDM
  239.  
  240. CheckBankUp MACRO  nbr:REQ        ;; Check bank, jumping only when the bank
  241.          JC      NewBankUp&nbr&   ;;  changes
  242. NewBankUp&nbr&Ret:
  243.          ENDM
  244.  
  245. SetBankUp MACRO  nbr:REQ          ;; Effect a bank change
  246.          ALIGN   16
  247. NewBankUp&nbr&:
  248.          DoBankUp
  249.          JMP     NewBankUp&nbr&Ret;; Return to main code
  250.          ENDM
  251.  
  252. DoBankDn MACRO
  253.          DEC     Bank             ;; Go to previous bank
  254.          PUSH    Bank             ;; Pass argument to function
  255.          PUSH    VDSP_SETBANK     ;; Dispatch code for SetBank
  256.          CALL    [Hardware]       ;; Set the new bank
  257.          ADD     SP, 4            ;; Remove arguments from the stack
  258.          ENDM
  259.  
  260. CheckBankDn MACRO  nbr:REQ        ;; Check bank, jumping only when the bank
  261.          JC      NewBankDn&nbr&   ;;  changes
  262. NewBankDn&nbr&Ret:
  263.          ENDM
  264.  
  265. SetBankDn MACRO  nbr:REQ          ;; Effect a bank change
  266.          ALIGN   16
  267. NewBankDn&nbr&:
  268.          DoBankDn
  269.          JMP     NewBankDn&nbr&Ret;; Return to main code
  270.          ENDM
  271.  
  272. SlideWindow MACRO
  273.          PUSH    Bank             ;; SlideWindow may adjust this
  274.          PUSH    VDSP_SLIDEWINDOW ;; Dispatch code for SlideWindow
  275.          CALL    [Hardware]       ;; Call the function
  276.          ADD     SP, 4            ;; Remove arguments from the stack
  277.                                   ;; AX = bank, ES:DI may be changed
  278.          MOV     Bank, AX         ;; Bank number may have changed
  279.          ENDM
  280.  
  281. COMMENT @
  282. Macros used with downward dithers from 24 bit sources
  283.  
  284. Get554 is used with 8 bit targets.  The result is placed in BX, where it
  285. can then be used as an index into a dither table.
  286.  
  287. Get555 is used with 15 bit (5-5-5) targets.
  288.  
  289. Get565 and Get565M are used with 16 bit (5-6-5) targets.
  290. @
  291. Get554   MACRO   offst:REQ             ;;
  292.          MOV     BH, [ESI+offst]       ;; Get R
  293.          SHR     BH, 3                 ;; Save 5 bits of R
  294.          MOV     BL, [ESI+offst+1]     ;; Get G
  295.          SHL     EBX, 5                ;; Save 5 bits of G
  296.          MOV     BL, [ESI+offst+2]     ;; Get B
  297.          SHR     EBX, 4                ;; Get 5-5-4
  298.          ENDM
  299.  
  300. Get555   MACRO   offst:REQ
  301.          MOV     AH, [ESI+offst]       ;; Get R
  302.          SHR     AH, 3                 ;; Save 5 bits of R
  303.          MOV     AL, [ESI+offst+1]     ;; Get G
  304.          SHL     EAX, 5                ;; Save 5 bits of G
  305.          MOV     AL, [ESI+offst+2]     ;; Get B
  306.          SHR     EAX, 3                ;; Get 5-5-5
  307.          ENDM
  308.  
  309. Get565   MACRO   offst:REQ
  310.          MOV     AH, [ESI+offst]       ;; Get R
  311.          SHR     AH, 3                 ;; Save 5 bits of R
  312.          MOV     AL, [ESI+offst+1]     ;; Get G
  313.          SHL     EAX, 6                ;; Save 6 bits of G
  314.          MOV     AL, [ESI+offst+2]     ;; Get B
  315.          SHR     EAX, 3                ;; Get 5-6-5
  316.          ENDM
  317.  
  318. Get565M  MACRO   offst:REQ
  319.          Get565  offst                 ;; Get 5-6-5 in Intel order
  320.          ROL     AX, 8                 ;; Put bytes in Motorola order
  321.          ENDM
  322.  
  323. COMMENT @
  324. Utility macros
  325.  
  326. The macro assumes CX bytes will be moved from DS:SI to ES:DI, and that
  327. DX is available.
  328. @
  329. @Equals  MACRO   p1:REQ, p2:REQ
  330.          EXITM   %( @InStr(,p1,p2) * @InStr(,p2,p1))
  331.          ENDM                     ;; of @Equals MACRO
  332.  
  333. CopyBytes MACRO
  334.          CLD
  335.          MOV     DX, CX           ;; Save byte count
  336.          SHR     CX, 2            ;; Byte count to DWORD count
  337.          REP     MOVSD            ;; Copy most of the bytes
  338.          MOV     CX, DX           ;; Restore byte count
  339.          AND     CX, 3            ;; 0-3 bytes left
  340.          SHR     CX, 1            ;; Check for 2 or more bytes left
  341.          REP     MOVSW            ;; Copy stray WORD, if any
  342.          ADC     CX, CX           ;; 0-1 bytes left
  343.          REP     MOVSB            ;; Copy stray byte, if any
  344.          ENDM                     ;; of CopyBytes MACRO
  345.  
  346. COMMENT @
  347. Macros used with decompressors that write directly to video adapter
  348. hardware.
  349. @
  350. GoOnScreenX MACRO
  351.          ALIGN   16
  352. GoOnScreen:
  353.          PUSH    CX               ;; Save register
  354.          MOV     CX, DirtyListNext     ;; Offset of next dirty list entry
  355.          SUB     CX, DirtyListFirst    ;; 4 * number of dirty list entries
  356.          JLE     NotDirty         ;; Skip if dirty list is empty
  357.  
  358.          PUSH    BX               ;; Save registers
  359.          PUSH    DX               ;;
  360.          PUSH    SI               ;;
  361.          PUSH    DI               ;;
  362.          PUSH    DS               ;;
  363.          PUSH    ES               ;;
  364.  
  365.          SHR     CX, 2            ;; Number of dirty list entries
  366.          MOV     DX, BadStart     ;; Onscreen offset of line start
  367.          NEG     DX               ;; Offset in offscreen buffer of bank split
  368.                                   ;; Merge adjacent ranges
  369.                                   ;; Split a range that splits a bank
  370.          MOV     DS, WORD PTR OffScreen +2  ;; Offscreen buffer is the source
  371.          MOV     SI, DirtyListFirst         ;; Point to first dirty list entry
  372.          LEA     DI, [SI-4]                 ;; Allow room for one split entry
  373. LoopNewRange:
  374.          MOV     AX, [SI]         ;; Get start of range
  375.          MOV     [DI], AX         ;; Save start of range
  376. LoopSameRange:
  377.          MOV     BX, [SI+2]       ;; Get end of range
  378.          ADD     SI, 4            ;; Point to next source slot
  379.          DEC     CX               ;; One fewer source slot
  380.          JLE     RangeDone        ;; Skip if no more source slots
  381.          CMP     BX, [SI]         ;; Is the next range adjacent to this?
  382.          JB      RangeDone        ;; Skip if ranges are not adjacent
  383.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  384. RangeDone:
  385.          CMP     AX, DX           ;; Might split occur in this range?
  386.          JGE     NoSplit          ;; Skip if split does not occur in this range
  387.          CMP     BX, DX           ;; Does split occur in this range?
  388.          JLE     NoSplit          ;; Skip if split does not occur in this range
  389.          MOV     [DI+2], DX       ;;
  390.          ADD     DI, 4            ;; Point to next target slot
  391.          MOV     [DI], DX         ;; Start of new range
  392. NoSplit:
  393.          MOV     [DI+2], BX       ;; Set right edge of range
  394.          ADD     DI, 4            ;; Point to next target slot
  395.          TEST    CX, CX           ;; Any source slots left?
  396.          JG      LoopNewRange     ;; Loop while source slots remain
  397.          OR      WORD PTR [DI], -1;; Terminate the list
  398.                                   ;; Make offsets relative to prior entries
  399.                                   ;; Convert ending offsets to byte sizes
  400.          LEA     CX, [DI+4]       ;; Compute number of list entries
  401.          SUB     CX, DirtyListFirst    ;; 4 * number of WORDs in the list
  402.          LEA     BX, [DI-2]       ;; Point to last WORD in list
  403.          SHR     CX, 1            ;; 2 * number of WORDs in list
  404.          DEC     CX               ;; Don't process first WORD
  405. LoopDiff:
  406.          MOV     AX, [BX-2]       ;; Get previous WORD in list
  407.          SUB     [BX], AX         ;; Compute difference from previous WORD
  408.          SUB     BX, 2            ;; Back up one WORD
  409.          DEC     CX               ;; One fewer WORD
  410.          JG      LoopDiff         ;; Loop once for each WORD in list
  411.                                   ;; Copy the first line of the pair
  412.          MOV     ES, SelVRAM      ;; VRAM is the target
  413.          MOV     DI, BadStart     ;;
  414.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  415. LoopCopy:
  416.          MOV     AX, [BX]         ;; Get offset of range start
  417.          TEST    AX, AX           ;; End of the list?
  418.          JL      DirtyDone        ;; Skip if end of the list
  419.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  420.          ADD     BX, 4            ;; Point to next list entry
  421.          ADD     SI, AX           ;; Locate data in offscreen buffer
  422.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  423.          CheckBankUp 01E          ;; Change bank if necessary
  424.          CopyBytes                ;; Copy the bytes
  425.          TEST    DI, DI           ;; Did range end on a bank boundary?
  426.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  427. NewBankUp02ERet:
  428.          JMP     LoopCopy         ;; Loop once for each range
  429.  
  430.          ALIGN   16
  431. DirtyDone:
  432.          TEST    DI, DI           ;; Did we cross the bank boundary?
  433.          JGE     DirtyCrossed     ;; Skip if we already crossed
  434.          DoBankUp                 ;; Cross the bank boundary
  435. DirtyCrossed:
  436.          POP     ES               ;; Restore registers
  437.          POP     DS               ;;
  438.          POP     DI               ;;
  439.          POP     SI               ;;
  440.          POP     DX               ;;
  441.          POP     BX               ;;
  442.          POP     CX               ;;
  443.          RETN                     ;; Return to caller without epilog
  444.  
  445.          ALIGN   16
  446. NotDirty:
  447.          POP     CX               ;; Restore register
  448.          DoBankUp                 ;; Go to next bank
  449.          RETN                     ;; Return to caller without epilog
  450.          ENDM                     ;; of GoOnScreenX
  451.  
  452. GoOnScreen2x MACRO
  453.          ALIGN   16
  454. GoOnScreen:
  455.          PUSH    CX               ;; Save register
  456.          MOV     CX, DirtyListNext     ;; Offset of next dirty list entry
  457.          SUB     CX, DirtyListFirst    ;; 4 * number of dirty list entries
  458.          JLE     NotDirty         ;; Skip if dirty list is empty
  459.  
  460.          PUSH    BX               ;; Save registers
  461.          PUSH    DX               ;;
  462.          PUSH    SI               ;;
  463.          PUSH    DI               ;;
  464.          PUSH    DS               ;;
  465.          PUSH    ES               ;;
  466.  
  467.          SHR     CX, 2            ;; Number of dirty list entries
  468.          MOV     BX, BadStart     ;; Onscreen offset of line start
  469.          MOV     DX, BX           ;; Copy onscreen offset of line start
  470.          NEG     DX               ;; Offset in offscreen buffer of bank split?
  471.          ADD     BX, WdBytes      ;; Onscreen offset of second line in pair
  472.          JC      FirstLineSplit   ;; Skip if first line in pair has bank split
  473.          MOV     DX, BX           ;; Copy onscreen offset of line start
  474.          NEG     DX               ;; Offset in offscreen buffer of bank split
  475. FirstLineSplit:
  476.                                   ;; Merge adjacent ranges
  477.                                   ;; Split a range that splits a bank
  478.          MOV     DS, WORD PTR OffScreen +2  ;; Offscreen buffer is the source
  479.          MOV     SI, DirtyListFirst         ;; Point to first dirty list entry
  480.          LEA     DI, [SI-4]                 ;; Allow room for one split entry
  481. LoopNewRange:
  482.          MOV     AX, [SI]         ;; Get start of range
  483.          MOV     [DI], AX         ;; Save start of range
  484. LoopSameRange:
  485.          MOV     BX, [SI+2]       ;; Get end of range
  486.          ADD     SI, 4            ;; Point to next source slot
  487.          DEC     CX               ;; One fewer source slot
  488.          JLE     RangeDone        ;; Skip if no more source slots
  489.          CMP     BX, [SI]         ;; Is the next range adjacent to this?
  490.          JB      RangeDone        ;; Skip if ranges are not adjacent
  491.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  492. RangeDone:
  493.          CMP     AX, DX           ;; Might split occur in this range?
  494.          JGE     NoSplit          ;; Skip if split does not occur in this range
  495.          CMP     BX, DX           ;; Does split occur in this range?
  496.          JLE     NoSplit          ;; Skip if split does not occur in this range
  497.          MOV     [DI+2], DX       ;;
  498.          ADD     DI, 4            ;; Point to next target slot
  499.          MOV     [DI], DX         ;; Start of new range
  500. NoSplit:
  501.          MOV     [DI+2], BX       ;; Set right edge of range
  502.          ADD     DI, 4            ;; Point to next target slot
  503.          TEST    CX, CX           ;; Any source slots left?
  504.          JG      LoopNewRange     ;; Loop while source slots remain
  505.                                   ;;
  506.          OR      WORD PTR [DI], -1;; Terminate the list
  507.                                   ;; Make offsets relative to prior entries
  508.                                   ;; Convert ending offsets to byte sizes
  509.          LEA     CX, [DI+4]       ;; Compute number of list entries
  510.          SUB     CX, DirtyListFirst    ;; 4 * number of WORDs in the list
  511.          LEA     BX, [DI-2]       ;; Point to last WORD in list
  512.          SHR     CX, 1            ;; 2 * number of WORDs in list
  513.          DEC     CX               ;; Don't process first WORD
  514. LoopDiff:
  515.          MOV     AX, [BX-2]       ;; Get previous WORD in list
  516.          SUB     [BX], AX         ;; Compute difference from previous WORD
  517.          SUB     BX, 2            ;; Back up one WORD
  518.          DEC     CX               ;; One fewer WORD
  519.          JG      LoopDiff         ;; Loop once for each WORD in list
  520.  
  521.                                   ;; Copy the first line of the pair
  522.          MOV     ES, SelVRAM      ;; VRAM is the target
  523.          MOV     DI, BadStart     ;;
  524.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  525. LoopCopy1:
  526.          MOV     AX, [BX]         ;; Get offset of range start
  527.          TEST    AX, AX           ;; End of the list?
  528.          JL      DirtyDone1       ;; Skip if end of the list
  529.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  530.          ADD     BX, 4            ;; Point to next list entry
  531.          ADD     SI, AX           ;; Locate data in offscreen buffer
  532.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  533.          CheckBankUp 01E          ;; Change bank if necessary
  534.          CopyBytes                ;; Copy the bytes
  535.          TEST    DI, DI           ;; Did range end on a bank boundary?
  536.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  537. NewBankUp02ERet:
  538.          JMP     LoopCopy1        ;; Loop once for each range
  539.  
  540.          ALIGN   16
  541.                                   ;; Copy the second line of the pair
  542. DirtyDone1:
  543.          MOV     AX, BadStart     ;; Onscreen offset of first line in pair
  544.          ADD     AX, WdBytes      ;; Onscreen offset of second line in pair
  545.          SUB     AX, DI           ;; Offset from current target pointer
  546.          ADD     DI, AX           ;; Add the offset back in
  547.          CheckBankUp 03E          ;; Change bank if necessary
  548.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  549.          MOV     BX, DirtyListFirst         ;; Point to first dirty list entry
  550.          SUB     BX, 4                      ;; Post-processed first entry
  551. LoopCopy2:
  552.          MOV     AX, [BX]         ;; Get offset of range start
  553.          TEST    AX, AX           ;; End of the list?
  554.          JL      DirtyDone2       ;; Skip if end of the list
  555.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  556.          ADD     BX, 4            ;; Point to next list entry
  557.          ADD     SI, AX           ;; Locate data in offscreen buffer
  558.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  559.          CheckBankUp 04E          ;; Change bank if necessary
  560.          CopyBytes                ;; Copy the bytes
  561.          TEST    DI, DI           ;; Did range end on a bank boundary?
  562.          JZ      NewBankUp05E     ;; Skip if range ended on a bank boundary
  563. NewBankUp05ERet:
  564.          JMP     LoopCopy2        ;; Loop once for each range
  565.  
  566.          ALIGN   16
  567. DirtyDone2:
  568.          TEST    DI, DI           ;; Did we cross the bank boundary?
  569.          JGE     DirtyCrossed2    ;; Skip if we already crossed
  570.          DoBankUp                 ;; Cross the bank boundary
  571. DirtyCrossed2:
  572.          POP     ES               ;; Restore registers
  573.          POP     DS               ;;
  574.          POP     DI               ;;
  575.          POP     SI               ;;
  576.          POP     DX               ;;
  577.          POP     BX               ;;
  578.          POP     CX               ;;
  579.          RETN                     ;; Return to caller without epilog
  580.  
  581.          ALIGN   16
  582. NotDirty:
  583.          POP     CX               ;; Restore register
  584.          DoBankUp                 ;; Go to next bank
  585.          RETN                     ;; Return to caller without epilog
  586.          ENDM                     ;; of GoOnScreen2x
  587.  
  588. COMMENT @
  589. Macro used to move pixels from onscreen buffer to offscreen buffer.
  590. @
  591. GoOffScreenX MACRO
  592.          LOCAL   NewBank
  593.          LOCAL   AllFits
  594.          LOCAL   ExitPath
  595.          ALIGN   16
  596. GoOffScreen:
  597.          PUSH    DS               ;; Save register
  598.          PUSH    CX               ;; Save register
  599.          PUSH    DX               ;; Save register
  600.          PUSH    SI               ;; Save register
  601.          PUSH    DI               ;; Save register
  602.          MOV     CX, AX           ;; Get byte count
  603.          MOV     DS, SelVRAM      ;; Point to onscreen buffer
  604.          MOV     SI, DI           ;; Copy target offset
  605.          SUB     SI, WORD PTR OffScreen ;; Compute offset from start of buffer
  606.          ADD     SI, BadStart     ;; Index into onscreen buffer
  607.          JC      NewBank          ;; Skip if we crossed a bank boundary
  608.          ADD     SI, CX           ;; Check ending source offset
  609.          JNC     AllFits          ;; Skip if no bank change required
  610.          JZ      AllFits          ;; Skip if no bank change required
  611.          PUSH    SI               ;; Save byte count for new bank
  612.          SUB     SI, CX           ;; Restore source pointer
  613.          MOV     CX, SI           ;; Compute byte count for old bank
  614.          NEG     CX               ;;
  615.          CopyBytes                ;; Copy the bytes
  616.          POP     CX               ;; Get byte count for new bank
  617. NewBank:
  618.          DoBankUp                 ;; Go to new bank
  619.          CopyBytes                ;; Copy the bytes
  620.          DoBankDn                 ;; Back to old bank
  621. ExitPath:
  622.          POP     DI               ;; Restore register
  623.          POP     SI               ;; Restore register
  624.          POP     DX               ;; Restore register
  625.          POP     CX               ;; Restore register
  626.          POP     DS               ;; Restore register
  627.          RETN                     ;; Return to caller without epilog
  628.  
  629.          ALIGN   16
  630. AllFits:                          ;; Source does not cross a bank boundary
  631.          SUB     SI, CX           ;; Restore source pointer
  632.          CopyBytes                ;; Copy the bytes
  633.          JMP     ExitPath         ;; Go to common exit
  634.          ENDM
  635.  
  636. COMMENT @
  637. Macro to compute number of source bytes processed, used for BMP banding
  638. @
  639. BytesUsed MACRO
  640.          XOR     EAX, EAX           ;; Zero out high order word
  641.          MOV     AX, WORD PTR Inbuf ;; Get offset of source pointer
  642.          SUB     ESI, EAX           ;; Compute number of source bytes processed
  643.          SHLD    EDX, ESI, 16       ;; Put high order word in DX
  644.          MOV     AX, SI             ;; Put low order word in AX
  645.          ENDM
  646.  
  647. COMMENT @
  648. Macros for moving pixels to and from overscan area
  649.  
  650. Note that the 2X versions of the macros should not be used with 8-bit
  651. targets since dithering will produce different target values for the
  652. same source.
  653.  
  654. We generate error messages only in the SavePixels macro, figuring they
  655. would be superfluous in RestPixels.
  656. @
  657. SaveOneRow MACRO
  658. j        =       0
  659.          REPEAT  Limit
  660.          MOV     EAX, ES:[DI+j]   ;; Get 4 bytes
  661.          MOV     OverScan[i], EAX ;; Save the bytes
  662. i        =       i + 4
  663. j        =       j + 4
  664.          ENDM                     ;; of REPEAT Limit
  665.          ENDM
  666.  
  667. RestOneRow MACRO Width:REQ
  668. j        =       0
  669.          WHILE   j  LT  Width - Width MOD 4
  670.          MOV     EAX, OverScan[i] ;; Get 4 bytes
  671.          MOV     ES:[DI+j], EAX   ;; Restore the bytes
  672. i        =       i + 4
  673. j        =       j + 4
  674.          ENDM                     ;; of WHILE  j  LT  Width - Width MOD 4
  675.          IF      j  LT  Width - Width MOD 2
  676.          MOV     AX, WORD PTR OverScan[i]   ;; Get 2 bytes
  677.          MOV     ES:[DI+j], AX              ;; Restore the bytes
  678. i        =       i + 2
  679. j        =       j + 2
  680.          ENDIF                    ;; of IF      j  LT  Width - Width MOD 2
  681.          IF      j  LT  Width
  682.          MOV     AL, BYTE PTR OverScan[i]   ;; Get 1 byte
  683.          MOV     ES:[DI+j], AL              ;; Restore the byte
  684. i        =       i + 1
  685. j        =       j + 1
  686.          ENDIF                    ;; of IF      j  LT  Width
  687. i        =       (i + 3) AND 0FFFCH    ;; Round up to multiple of 4
  688.          ENDM                     ;; of RestOneRow MACRO
  689.  
  690. SavePixels MACRO Width:REQ, Height:REQ, Increment:REQ
  691. i        =       0
  692. Limit    =       (((Width) + 3) AND 0FFFCH) / 4
  693. ReqdSize TEXTEQU %(Limit * Height)
  694.          IFNDEF  OverScan
  695. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  696.          EXITM
  697.          ENDIF
  698.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  699. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  700.          EXITM
  701.          ENDIF
  702.          REPEAT  Height
  703.          ADD     DI, Increment
  704.          SaveOneRow
  705.          ENDM                     ;; of REPEAT Height
  706.          ENDM                     ;; of MACRO
  707.  
  708. RestPixels MACRO Width:REQ, Height:REQ, Increment:REQ
  709. i        =       0
  710. Limit    =       (((Width) + 3) AND 0FFFCH) / 4
  711. ReqdSize TEXTEQU %(Limit * Height)
  712.          IFNDEF  OverScan         ;; If variable not defined
  713.          EXITM                    ;; Avoid further error messages
  714.          ENDIF
  715.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  716.          EXITM
  717.          ENDIF
  718.          REPEAT  Height
  719.          ADD     DI, Increment
  720.          RestOneRow (Width)
  721.          ENDM                     ;; of REPEAT Height
  722.          ENDM                     ;; of MACRO
  723.  
  724. SavePix2X MACRO Width:REQ, Height:REQ, Increment:REQ
  725. i        =       0
  726. Limit    =       (((Width) * 2 + 3) AND 0FFFCH) / 4
  727. ReqdSize TEXTEQU %(Limit * Height)
  728.          IFNDEF  OverScan
  729. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  730.          EXITM
  731.          ENDIF
  732.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  733. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  734.          EXITM
  735.          ENDIF
  736.          REPEAT  Height
  737.          ADD     DI, Increment
  738.          ADD     DI, Increment
  739.          SaveOneRow
  740.          ENDM                     ;; of REPEAT Height
  741.          ENDM                     ;; of MACRO
  742.  
  743. RestPix2X MACRO Width:REQ, Height:REQ, Increment:REQ
  744. i        =       0
  745. Limit    =       (((Width) * 2 + 3) AND 0FFFCH) / 4
  746. ReqdSize TEXTEQU %(Limit * Height)
  747.          IFNDEF  OverScan         ;; If variable not defined
  748.          EXITM                    ;; Avoid further error messages
  749.          ENDIF
  750.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  751.          EXITM
  752.          ENDIF
  753.          PUSH    BX               ;; Save register
  754.          MOV     BX, Increment    ;; Initialize index register
  755. j        =       0
  756.          REPEAT  Limit            ;; Restore row #2 from row #1
  757.          MOV     EAX, ES:[DI+j]   ;; Get 4 uncorrupted bytes
  758.          MOV     ES:[DI+BX+j], EAX;; Restore the bytes
  759. j        =       j + 4
  760.          ENDM                     ;; of REPEAT Limit
  761.          REPEAT  Height - 1
  762.          ADD     DI, BX
  763.          ADD     DI, BX
  764. j        =       0
  765.          REPEAT  Limit
  766.          MOV     EAX, OverScan[i] ;; Get 4 saved bytes
  767.          MOV     ES:[DI+j], EAX   ;; Restore the bytes to row #1
  768.          MOV     ES:[DI+BX+j], EAX;; Restore the bytes to row #2
  769. i        =       i + 4
  770. j        =       j + 4
  771.          ENDM                     ;; of REPEAT Limit
  772.          ENDM                     ;; of REPEAT Height
  773.          ADD     DI, BX           ;; Point to last row
  774.          ADD     DI, BX
  775.          RestOneRow ((Width) * 2)
  776.          POP     BX               ;; Restore register
  777.          ENDM                     ;; of MACRO
  778.